package com.ficus.road.maintain.core.dao;

import com.alibaba.fastjson.JSON;
import com.ficus.road.maintain.core.infrastructure.mongo.MongoDbConfig;
import com.github.pagehelper.PageInfo;
import com.mongodb.client.FindIterable;
import com.mongodb.client.MongoCursor;
import com.mongodb.client.result.DeleteResult;
import lombok.Getter;
import org.bson.Document;
import org.bson.conversions.Bson;
import org.bson.json.JsonWriterSettings;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.BulkOperations;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
import org.springframework.data.mongodb.core.query.BasicQuery;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;

/**
 * 数据库操作基类，封装最常用的CRUD方法
 *
 * @author siyuan.li
 * @date 2020/3/17
 */
@Component
@Scope("prototype")
public abstract class BaseMongoDao<T, PK extends Serializable> {

    @Autowired
    private MongoDbConfig mongoDbConfig;

    @Getter
    private Class<T> entityClass = null;

    public BaseMongoDao() {
        Class<?> c = getClass();
        Type type = c.getGenericSuperclass();
        if (type instanceof ParameterizedType) {
            Type[] parameterizedType = ((ParameterizedType) type)
                    .getActualTypeArguments();
            entityClass = (Class<T>) parameterizedType[0];
        }
    }

    @PostConstruct
    public void init() {
        indexOperation();
    }

    /**
     * 相关的索引操作
     */
    protected void indexOperation() {
    }

    protected MongoTemplate getMongoTemplate() {
        return mongoDbConfig.mongoTemplate();
    }

    protected String getCollectionName() {
        return getMongoTemplate().getCollectionName(entityClass);
    }

    /**
     * 查询所有列表
     *
     * @return
     */
    public List<T> findAll() {
        return getMongoTemplate().findAll(entityClass, getCollectionName());
    }

    /**
     * 删除所有记录
     *
     * @return
     */
    public long deleteAll() {
        DeleteResult remove = getMongoTemplate().remove(new Query(), getCollectionName());
        return remove.getDeletedCount();
    }

    /**
     * 按条件查询
     *
     * @param query
     * @return
     */
    public List<T> findByQuery(Query query) {
        return getMongoTemplate().find(query, entityClass, getCollectionName());
    }

    /**
     * 按条件查询distinct数据
     *
     * @param query
     * @return
     */
    public List findDistinctByQuery(Query query, String field) {
        return getMongoTemplate().findDistinct(query, field, getCollectionName(), entityClass);
//        return getMongoTemplate().find(query, entityClass, getCollectionName());
    }


    public List<T> aggregate(Aggregation aggregation) {
        AggregationResults<T> aggregate = getMongoTemplate().aggregate(aggregation, getCollectionName(), entityClass);
        return aggregate.getMappedResults();
    }

    public <T> List<T> findAggregateList(Aggregation aggregation, Class<T> clazz) {
        String collectionName = getCollectionName();
        AggregationResults<T> aggregate = getMongoTemplate().aggregate(aggregation, collectionName, clazz);
        List<T> customerDetails = aggregate.getMappedResults();
        return customerDetails;
    }

    public AggregationResults<Document> aggregateToDocument(Aggregation aggregation) {
        return getMongoTemplate().aggregate(aggregation, getCollectionName(), Document.class);
    }


    /**
     * 按条件通过collection查询
     *
     * @param query
     * @return
     */
    public List<T> findByQuery(Bson query) {
        FindIterable<Document> findIterable = getMongoTemplate().getCollection(getCollectionName()).find(query);
        List<T> dataList = new ArrayList<T>();
        MongoCursor<Document> cursor = findIterable.iterator();
        JsonWriterSettings settings = JsonWriterSettings.builder()
                .objectIdConverter((value, writer) -> writer.writeNumber(value.toString()))
                .build();
        while (cursor.hasNext()) {
            String realJson = cursor.next().toJson(settings);
            dataList.add((T) JSON.parseObject(realJson, entityClass));
        }
        return dataList;
    }

    /**
     * 插入实体
     *
     * @param entity
     * @return
     */
    public T insert(T entity) {
        getMongoTemplate().insert(entity, getCollectionName());
        return entity;
    }

    /**
     * 插入实体
     *
     * @param entitys
     * @return
     */
    public Collection<T> insert(List<T> entitys) {
        Collection<T> tCollection = getMongoTemplate().insert(entitys, getCollectionName());
        return tCollection;
    }

    /**
     * 按id更新单条记录
     *
     * @param id
     * @param update
     */
    public void update(String id, Update update) {
        getMongoTemplate().updateFirst(new Query(Criteria.where("id").is(id)),
                update, entityClass, getCollectionName());

    }

    /**
     * 保存或更新实体
     *
     * @param entity
     */
    public T saveOrUpdate(T entity) {
        getMongoTemplate().save(entity, getCollectionName());
        return entity;
    }

    public void batchUpdate(List<T> entities, Query query, Update update) {
        BulkOperations bulkOperations = getMongoTemplate().bulkOps(BulkOperations.BulkMode.UNORDERED, entityClass);
        bulkOperations.upsert(query, update);
        bulkOperations.execute();
    }

    /**
     * 根据条件查询单个对象
     *
     * @param
     * @return
     */
    public T findOne(Query query) {
        T entity = (T) getMongoTemplate().findOne(query, entityClass, getCollectionName());
        return entity;
    }

    /**
     * 根据id查询单个对象
     *
     * @param id
     * @return
     */
    public T fetch(PK id) {
        return getMongoTemplate().findById(id, entityClass, getCollectionName());
    }

    /**
     * 根据id删除单个对象
     *
     * @param id
     */
    public void deleteById(PK id) {
        Criteria criteria = Criteria.where("_id").is(id);
        Query query = new Query(criteria);
        if (query != null && getMongoTemplate().findOne(query, entityClass, getCollectionName()) != null) {
            getMongoTemplate().remove(Objects.requireNonNull(getMongoTemplate().findOne(query, entityClass, getCollectionName())), getCollectionName());
        }
    }

    /**
     * 获取列表分页
     *
     * @param currentPage
     * @param pageSize
     * @return
     */
    public PageInfo<T> findListByPage(int currentPage, int pageSize) {
        Query query = new Query();
        return queryPage(currentPage, pageSize, query);
    }

    /**
     * 按条件查询并分页
     *
     * @param currentPage
     * @param pageSize
     * @param query
     * @return
     */
    public PageInfo<T> queryPage(int currentPage, int pageSize, Query query) {
        return queryPage(currentPage, pageSize, query.getQueryObject());
    }

    /**
     * 按条件查询并分页
     *
     * @param currentPage
     * @param pageSize
     * @param query
     * @return
     */
    public PageInfo<T> queryPage(int currentPage, int pageSize, Bson query) {
        int start = currentPage * pageSize;
        return queryLimit(start, pageSize, query);
    }

    /**
     * 按条件查询
     *
     * @param start
     * @param limit
     * @param queryBson
     * @return
     */
    public PageInfo<T> queryLimit(int start, int limit, Bson queryBson) {
        Query query = new BasicQuery((Document) queryBson);
        query.skip(start).limit(limit);
        long count = getMongoTemplate().count(query, entityClass, getCollectionName());
        List<T> dataList = getMongoTemplate().find(query, entityClass, getCollectionName());
        PageInfo<T> info = new PageInfo<T>(new ArrayList<T>());
        info.setTotal(count);
        info.setList(dataList);
        return info;
    }

    /**
     * 按条件查询
     *
     * @param start
     * @param limit
     * @param queryBson
     * @return
     */
    public PageInfo<T> queryLimit(int start, int limit, Bson queryBson, Sort sort) {
        Query query = new BasicQuery((Document) queryBson);
        query.skip(start).limit(limit);
        long count = getMongoTemplate().count(query, entityClass, getCollectionName());
        List<T> dataList = getMongoTemplate().find(query.with(sort), entityClass, getCollectionName());
        PageInfo<T> info = new PageInfo<T>(new ArrayList<T>());
        info.setTotal(count);
        info.setList(dataList);
        return info;
    }

    /**
     * 计算统计条数
     *
     * @param query
     * @return
     */
    public long count(Bson query) {
        return getMongoTemplate().getCollection(getCollectionName()).count(query);
    }

    /**
     * 计算统计条数
     *
     * @param query
     * @return
     */
    public long count(Query query) {
        return getMongoTemplate().count(query, entityClass, getCollectionName());
    }

}